package cn.usbtg.poi.ext;

import cn.usbtg.poi.ext.exception.UnsupportedVersionException;
import cn.usbtg.sutil.EmptyUtil;
import org.apache.poi.hssf.OldExcelFormatException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * apache poi 操作excel工具类
 */
public class POIReadUtil {

    /**
     * 获取工作表
     *
     * @param filePath excel文件路径
     * @return Workbook
     */
    public static Workbook getWorkbook(String filePath) {
        return getWorkbook(new File(filePath));
    }

    /**
     * 获取工作表
     *
     * @param file excel文件
     * @return Workbook
     */
    public static Workbook getWorkbook(File file) {
        try {
            FileInputStream is = new FileInputStream(file);
            return WorkbookFactory.create(is);
        } catch (InvalidFormatException | IOException e) {
            e.printStackTrace();
        } catch (OldExcelFormatException e) {
            throw new UnsupportedVersionException("不支持的excel版本，请使用97-03以上版本");
        }
        return null;
    }

    /**
     * 获取工作薄工作表
     *
     * @param workbook   工作簿对象
     * @param beginIndex 工作表其实下标
     * @param endIndex   工作表结束下标
     * @return List<Sheet>
     */
    public static List<Sheet> getSheets(Workbook workbook, int beginIndex, int endIndex) {
        if (EmptyUtil.isEmpty(workbook)) return null;
        List<Sheet> sheets = new ArrayList<>();

        endIndex = endIndex >= 0 ? endIndex : 0;
        endIndex = endIndex <= workbook.getNumberOfSheets() - 1 ? endIndex : workbook.getNumberOfSheets() - 1;
        beginIndex = beginIndex >= 0 ? beginIndex : 0;
        beginIndex = beginIndex < endIndex ? beginIndex : endIndex;

        for (int i = beginIndex; i <= endIndex; i++) {
            sheets.add(workbook.getSheetAt(i));
        }
        return sheets;
    }

    /**
     * 获取工作薄工作表
     *
     * @param workbook   工作簿对象
     * @param beginIndex 工作表起始下标
     * @return List<Sheet>
     */
    public static List<Sheet> getSheets(Workbook workbook, int beginIndex) {
        return getSheets(workbook, beginIndex, workbook.getNumberOfSheets() - 1);
    }

    /**
     * 获取工作薄工作表
     *
     * @param workbook 工作簿对象
     * @return List<Sheet>
     */
    public static List<Sheet> getSheets(Workbook workbook) {
        return getSheets(workbook, 0);
    }

    /**
     * 获取工作表行
     *
     * @param sheet      工作表对象
     * @param beginIndex 行起始下标
     * @param endIndex   行结束下标
     * @return List<Row>
     */
    public static List<Row> getRows(Sheet sheet, int beginIndex, int endIndex) {
        List<Row> rows = new ArrayList<>();

        endIndex = endIndex < 0 ? 0 : endIndex;
        endIndex = endIndex > sheet.getPhysicalNumberOfRows() - 1 ? sheet.getPhysicalNumberOfRows() - 1 : endIndex;

        beginIndex = beginIndex >= 0 ? beginIndex : 0;
        beginIndex = beginIndex < endIndex ? beginIndex : endIndex;

        for (int i = beginIndex; i <= endIndex; i++) {
            rows.add(sheet.getRow(i));
        }
        return rows;
    }

    /**
     * 获取工作表行
     *
     * @param sheet      工作表对象
     * @param beginIndex 行起始下标
     * @return List<Row>
     */
    public static List<Row> getRows(Sheet sheet, int beginIndex) {
        return getRows(sheet, beginIndex, sheet.getPhysicalNumberOfRows() - 1);
    }

    /**
     * 获取工作表行
     *
     * @param sheet 工作表对象
     * @return List<Row>
     */
    public static List<Row> getRows(Sheet sheet) {
        return getRows(sheet, 0);
    }

    /**
     * 获取行单元格
     *
     * @param row        行对象
     * @param beginIndex 单元格起始下标
     * @param endIndex   单元格结束下标
     * @return List<Cell>
     */
    public static List<Cell> getCells(Row row, int beginIndex, int endIndex) {
        List<Cell> cells = new ArrayList<>();
        endIndex = endIndex < 0 ? 0 : endIndex;
        endIndex = endIndex > row.getPhysicalNumberOfCells() - 1 ? row.getPhysicalNumberOfCells() - 1 : endIndex;

        beginIndex = beginIndex >= 0 ? beginIndex : 0;
        beginIndex = beginIndex < endIndex ? beginIndex : endIndex;

        for (int i = beginIndex; i <= endIndex; i++) {
            cells.add(row.getCell(i));
        }
        return cells;
    }

    /**
     * 获取行单元格
     *
     * @param row        行对象
     * @param beginIndex 单元格起始下标
     * @return List<Cell>
     */
    public static List<Cell> getCells(Row row, int beginIndex) {
        return getCells(row, beginIndex, row.getPhysicalNumberOfCells() - 1);
    }

    /**
     * 获取行单元格
     *
     * @param row 行对象
     * @return List<Cell>
     */
    public static List<Cell> getCells(Row row) {
        return getCells(row, 0);
    }

    /**
     * 获取单元格数据内容为字符串类型的数据
     *
     * @param cell Excel单元格
     * @return String 单元格数据内容
     */
    public static String getCellValue2String(Cell cell) throws Exception {
        if (EmptyUtil.isEmpty(cell)) return "";

        String cellStrValue;
        try {
            switch (cell.getCellType()) {
                case Cell.CELL_TYPE_STRING:
                    cellStrValue = cell.getStringCellValue();
                    cellStrValue = cellStrValue.charAt(0) == 39 ? cellStrValue.substring(1) : cellStrValue;//处理单元格首字符加引号强制转换成文本格式
                    break;
                case Cell.CELL_TYPE_NUMERIC:
                    if (DateUtil.isCellDateFormatted(cell)) {
                        cellStrValue = cn.usbtg.sutil.DateUtil.formatDateTime(cell.getDateCellValue());
                    } else {
                        //cellStrValue = String.valueOf(cell.getNumericCellValue()); //通俗处理方式原始格式

                        //格式化成两位小数或整数
                        double cellValue = cell.getNumericCellValue();
                        String formatStr = cellValue % 1 > 0 ? "#.00" : "#";
                        cellStrValue = new DecimalFormat(formatStr).format(cellValue);
                    }
                    break;
                case Cell.CELL_TYPE_BOOLEAN:
                    cellStrValue = String.valueOf(cell.getBooleanCellValue());
                    break;
                case Cell.CELL_TYPE_FORMULA:
                    try {
                        cellStrValue = String.valueOf(cell.getNumericCellValue());
                    } catch (IllegalStateException e) {
                        cellStrValue = String.valueOf(cell.getRichStringCellValue());
                    }
                    break;
                default:
                    cellStrValue = "";
                    break;
            }
        } catch (Exception e) {
            throw new Exception("单元格格式错误");
        }

        return cellStrValue;
    }

    /**
     * 获取单元格数据内容为boolean类型的数据
     *
     * @param cell Excel单元格
     * @return Boolean 单元格数据内容
     */
    public static Boolean getCellValue2Boolean(Cell cell) throws Exception {
        if (EmptyUtil.isEmpty(cell)) return false;
        if (cell.getCellType() != Cell.CELL_TYPE_BOOLEAN) {
            throw new Exception("单元格格式错误");
        }
        boolean cellValue;
        try {
            cellValue = cell.getBooleanCellValue();
        } catch (Exception e) {
            throw new Exception("单元格格式错误");
        }
        return cellValue;
    }

    /**
     * 获取单元格数据内容为Date类型的数据
     *
     * @param cell Excel单元格
     * @return Date 单元格数据内容
     */
    public static Date getCellValue2Date(Cell cell) throws Exception {
        if (EmptyUtil.isEmpty(cell)) return null;
        Date cellValue;
        try {
            if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC && DateUtil.isCellDateFormatted(cell)) {
                cellValue = cell.getDateCellValue();
            } else {
                String cellValueStr = getCellValue2String(cell);
                if (EmptyUtil.isEmpty(cellValueStr)) {
                    cellValue = null;
                } else {
                    cellValueStr = cellValueStr.trim();

                    String formatSplit = "-";
                    if (cellValueStr.contains("/")) {
                        formatSplit = "/";
                    }

                    boolean hasDate = cellValueStr.contains(formatSplit) || cellValueStr.length() == 4;
                    boolean hasTime = cellValueStr.contains(" ") || cellValueStr.contains(":");

                    String dateStr = "";
                    String timeStr = "";
                    if (hasDate && hasTime) {
                        dateStr = cellValueStr.substring(0, cellValueStr.indexOf(" ")).trim();
                        timeStr = cellValueStr.substring(cellValueStr.indexOf(" ")).trim();
                    } else if (hasDate) {
                        dateStr = cellValueStr;
                        timeStr = "";
                    } else if (hasTime) {
                        dateStr = "";
                        timeStr = cellValueStr;
                    }

                    String formatStrL = "";
                    if (EmptyUtil.isEmpty(dateStr)) {//空
                        formatStrL = "";
                    } else if (dateStr.split(formatSplit).length == 1) {//年
                        formatStrL = "yyyy";
                    } else if (dateStr.split(formatSplit).length == 2) {//年-月
                        formatStrL = "yyyy" + formatSplit + "MM";
                    } else if (dateStr.split(formatSplit).length == 3) {//年-月-日
                        formatStrL = "yyyy" + formatSplit + "MM" + formatSplit + "dd";
                    }

                    String formatStrR = "";
                    if (EmptyUtil.isEmpty(timeStr)) {//无
                        formatStrR = "";
                    } else if (timeStr.split(":").length == 1) {//时
                        formatStrR = "HH";
                    } else if (timeStr.split(":").length == 2) {//时:分
                        formatStrR = "HH:mm";
                    } else if (timeStr.split(":").length == 3) {//时:分:秒
                        formatStrR = "HH:mm:ss";
                    }

                    cellValue = new SimpleDateFormat(formatStrL + " " + formatStrR).parse(dateStr + " " + timeStr);
                }
            }
        } catch (Exception e) {
            throw new Exception("单元格格式错误");
        }

        return cellValue;
    }

    /**
     * 获取单元格数据内容为double类型的数据
     *
     * @param cell Excel单元格
     * @return double 单元格数据内容
     */
    public static double getCellValue2Double(Cell cell) throws Exception {
        if (EmptyUtil.isEmpty(cell)) return 0;
        if (cell.getCellType() != Cell.CELL_TYPE_NUMERIC) {
            throw new Exception("单元格格式错误");
        }
        double cellValue;
        try {
            cellValue = cell.getNumericCellValue();
        } catch (Exception e) {
            throw new Exception("单元格格式错误");
        }
        return cellValue;
    }

    /**
     * 获取单元格数据内容为long类型的数据
     *
     * @param cell Excel单元格
     * @return long 单元格数据内容
     */
    public static long getCellValue2Long(Cell cell) throws Exception {
        long cellValue;
        try {
            cellValue = Double.valueOf(String.valueOf(getCellValue2Double(cell))).longValue();
        } catch (Exception e) {
            throw new Exception("单元格格式错误");
        }
        return cellValue;
    }

    /**
     * 根据列索引从行获取单元格的值
     *
     * @param row   行
     * @param index 列索引（从0开始，如0、1、2）
     * @return
     */
    public static String getCellValue2StringByIndex(Row row, int index) throws Exception {
        return getCellValue2String(row.getCell(index));
    }

    /**
     * 根据列索引从行获取单元格的值
     *
     * @param row   行
     * @param index 列索引（从0开始，如0、1、2）
     * @return
     */
    public static boolean getCellValue2BooleanByIndex(Row row, int index) throws Exception {
        return getCellValue2Boolean(row.getCell(index));
    }

    /**
     * 根据列索引从行获取单元格的值
     *
     * @param row   行
     * @param index 列索引（从0开始，如0、1、2）
     * @return
     */
    public static Date getCellValue2DateByIndex(Row row, int index) throws Exception {
        return getCellValue2Date(row.getCell(index));
    }

    /**
     * 根据列索引从行获取单元格的值
     *
     * @param row   行
     * @param index 列索引（从0开始，如0、1、2）
     * @return
     */
    public static double getCellValue2DoubleByIndex(Row row, int index) throws Exception {
        return getCellValue2Double(row.getCell(index));
    }

    /**
     * 根据列索引从行获取单元格的值
     *
     * @param row   行
     * @param index 列索引（从0开始，如0、1、2）
     * @return
     */
    public static long getCellValue2LongByIndex(Row row, int index) throws Exception {
        return getCellValue2Long(row.getCell(index));
    }

    /**
     * 根据列名称从行获取单元格的值
     *
     * @param row    行
     * @param column 列名称（如 A、B、C）
     * @return
     */
    public static String getCellValue2StringByColumn(Row row, String column) throws Exception {
        return getCellValue2StringByIndex(row, ExcelUtil.columnToIndex(column));
    }

    /**
     * 根据列名称从行获取单元格的值
     *
     * @param row    行
     * @param column 列名称（如 A、B、C）
     * @return
     */
    public static boolean getCellValue2BooleanByColumn(Row row, String column) throws Exception {
        return getCellValue2BooleanByIndex(row, ExcelUtil.columnToIndex(column));
    }

    /**
     * 根据列名称从行获取单元格的值
     *
     * @param row    行
     * @param column 列名称（如 A、B、C）
     * @return
     */
    public static Date getCellValue2DateByColumn(Row row, String column) throws Exception {
        return getCellValue2DateByIndex(row, ExcelUtil.columnToIndex(column));
    }

    /**
     * 根据列名称从行获取单元格的值
     *
     * @param row    行
     * @param column 列名称（如 A、B、C）
     * @return
     */
    public static double getCellValue2DoubleByColumn(Row row, String column) throws Exception {
        return getCellValue2DoubleByIndex(row, ExcelUtil.columnToIndex(column));
    }

    /**
     * 根据列名称从行获取单元格的值
     *
     * @param row    行
     * @param column 列名称（如 A、B、C）
     * @return
     */
    public static long getCellValue2LongByColumn(Row row, String column) throws Exception {
        return getCellValue2LongByIndex(row, ExcelUtil.columnToIndex(column));
    }
}